lexer grammar ProgmaLexer;

options {
  language = Java;
  superClass = ProgmaBaseLexer;
}

@header {
package progma.lang.compiler;
}

@members {
    boolean needNewline = true;
    
    @Override
    public Token nextToken() {
        Token t = super.nextToken();
        if(t == Token.EOF_TOKEN && needNewline) {
            needNewline = false;
            return new CommonToken(NL, "\\n");
        }
        return t;
    }
}

LPAREN
    : '('// {implicitLineJoiningLevel++;}
    ;

RPAREN
    : ')'// {implicitLineJoiningLevel--;}
    ;

LCURLY
    : '{'// {implicitLineJoiningLevel++;}
    ;

RCURLY
    : '}'// {implicitLineJoiningLevel--;}
    ;

LBRACK
    : '[' {implicitLineJoiningLevel++;}
    ;

RBRACK
    : ']' {implicitLineJoiningLevel--;}
    ;

STRING
    : '\"' (~'\"')* '\"'
    ;

CHAR
    : '\'' ~('\'' | '\n' | '\t') '\''
    ;

BOOL
    : 'true'
    | 'false'
    ;

NULL
    : 'null'
    ;

MODULE
    : 'module'
    ;

IMPORT
    : 'import'
    ;

INFIX
    : 'infix'
    ;

CLASS
    : 'class'
    ;

TRY
    : 'try'
    ;

CATCH
    : 'catch'
    ;

FINALLY
    : 'finally'
    ;

WHILE
    : 'while'
    ;

DO
    : 'do'
    ;

IF
    : 'if'
    ;

THEN
    : 'then'
    ;

ELSE
    : 'else'
    ;

MATCH
    : 'match'
    ;

CASE
    : 'case'
    ;

AT
    : '@'
    ;

EQUALS
    : '='
    ;

DOT
    : '.'
    ;

COMMA
    : ','
    ;

COLONEQUALS
    : ':='
    ;

COLON
    : ':'
    ;

SEMICOLON
    : ';'
    ;

ARROW
    : '=>'
    ;

WILD
    : '_'
    ;

LAMBDASTART
    : '#{'
    ;

BACKQUOTE
    : '`'
    ;

VAR
    : (NAME '/')* ID
    ;

fragment
ID
    : ( '_' | 'a' .. 'z' | 'A' .. 'Z')
      ( 'a' .. 'z' | 'A' .. 'Z' | '_' | VAROPERATORS | '0' .. '9' )*
    ;

fragment
VAROPERATORS
    : ('!' | '?' | '%' | '&' | '*' | '-' | '+' | '>' | '<')+
    ;

fragment
NAME
    : ( 'a' .. 'z' | 'A' .. 'Z' | '_')
      ( 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' )*
    ;

OPERATOR
    : ('!' | '?' | '%' | '&' | '|' | '*' | '-' | '+' | '/' | '>' | '<' | '^' | '#')+
      (EQUALS)*
    ;

NUMBER
    : INTEGER
    | DOUBLE
    ;

fragment
INTEGER
    : ('+' | '-')? '0' ('0' .. '9')*
    | ('+' | '-')? ('1' .. '9') ('0' .. '9')*
    | ('+' | '-')? '0' ('x' | 'X') HEXA+
    ;

fragment
HEXA
    : ('a' .. 'f') | ('A' .. 'F') | ('0' .. '9')
    ;

fragment
DOUBLE
    : ('+' | '-')? ('0' .. '9')+ DOT ('0' .. '9')+ EXPONENT?
    ;

fragment
EXPONENT
    : ('e' | 'E') ('+' | '-')? ('0' .. '9')+
    ;

NL
@init {
int charpos = getCharPositionInLine();
}
    : (('\u000C')?('\r')? '\n' )+
      {if ( charpos == 0 || implicitLineJoiningLevel > 0 )
           $channel = HIDDEN;
      }
    ;

WS
    : {getCharPositionInLine() > 0}? => (' '|'\t'|'\u000C')+ {$channel = HIDDEN;}
    | (' '|'\t')+ NL? {$channel = HIDDEN;}
    ;

COMMENT
@init {
    $channel=HIDDEN;
}
    : {getCharPositionInLine() <= 0}? => (' '|'\t'|'\u000C')* '//' (~'\n')* '\n'+
    | {getCharPositionInLine() > 0}? => '//' (~'\n')* // '\n'+
    ;

MULTICOMMENT
@init {
    $channel = HIDDEN;
}
    : '/*' .* '*/' (NL)*
    ;